package jmind.core.spring;

import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;

import jmind.core.log.LogUtil;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.ResourceEntityResolver;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.util.ClassUtils;

/**
 * 
 * @author xieweibo
 *  <bean id="serviceLocator" class="jmind.core.spring.SpringBeanLocator" 
        factory-method="getInstance"    /> 
 */
public class SpringBeanLocator implements ApplicationContextAware, BeanLocator {

    private final static SpringBeanLocator INSTANCE = new SpringBeanLocator();

    private ConfigurableApplicationContext context;

    public static SpringBeanLocator getInstance() {
        return INSTANCE;
    }

    /*
     * 注入ApplicationContext
     */
    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        //在加载Spring时自动获得context
        this.context = (ConfigurableApplicationContext) context;
    }

    public  ApplicationContext getContext(){
        return context;
    }

    public <T> T getBean(Class<T> requiredType) {
        return context.getBean(requiredType);
    }

    @SuppressWarnings("unchecked")
    public <T> T getBean(final String beanName) {
        return (T) context.getBean(beanName);
    }

    public <T> T getBean(String name, Class<T> requiredType) {
        return context.getBean(name, requiredType);
    }

    public boolean containsBean(String name) {
        if (context == null)
            return false;
        return context.containsBean(name);
    }

 
    public <T> String[] getBeanNames(Class<T> clazz){
      return  context.getBeanNamesForType(clazz);
    }
    /**
	*  BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, AbstractWorker.class, false, false);
	**/
    public <T> Map<String,T>  getBeans(Class<T> clazz){
       return context.getBeansOfType(clazz);
    }

    /** 
     * 向spring的beanFactory动态地装载bean 
     * @param configLocationString 要装载的bean所在的xml配置文件位置。 
    spring配置中的contextConfigLocation，同样支持诸如"/WEB-INF/ApplicationContext-*.xml"的写法。 
    @link http://elicer.iteye.com/blog/438026
    @link http://zhyi-12.iteye.com/blog/953295
          加载push的配置文件
       SpringBeanLocator.getInstance().loadConfig("/WEB-INF/spring/push-context.xml");
     */
    public void loadConfig(String... configLocationString) {
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(
                (BeanDefinitionRegistry) context.getBeanFactory());
        beanDefinitionReader.setResourceLoader(context);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(context));
        try {
            LogUtil.debug("dynamic load {}", configLocationString);
            for (String configLocation : configLocationString)
                beanDefinitionReader.loadBeanDefinitions(context.getResources(configLocation));

        } catch (BeansException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 加载包下面的类
     * @param packages
     */
    public void loadPackages(String... packages){
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(
                (BeanDefinitionRegistry) context.getBeanFactory());
        beanDefinitionReader.setResourceLoader(context);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(context));
//         ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
//         MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(
//                resourcePatternResolver);
        Environment environment = new StandardEnvironment();
        for(String pg:packages){
            String path = ClassUtils.convertClassNameToResourcePath(environment.resolveRequiredPlaceholders(pg)) + "/**/*.class";
            // path=ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX+path;
            try {
                beanDefinitionReader.loadBeanDefinitions(context.getResources(path));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
// http://wenku.baidu.com/link?url=KEFaIwqStSHzNM99JGlb5CnKoEaYnaTlyVZiAOrTxKtEVvOUmXptJgcZj-JKChJzHtmhq_9PVJi-y7-Q7JLfpBpIWXNrOvX5715XnqTNvlq
    public void loadBean(Class<?> clazz) {
        BeanDefinitionBuilder userBeanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
        // 增加对象
        //  userBeanDefinitionBuilder.addPropertyReference(name, beanName)
        DefaultListableBeanFactory acf = (DefaultListableBeanFactory) context.getAutowireCapableBeanFactory();
        acf.registerBeanDefinition(clazz.getName(), userBeanDefinitionBuilder.getRawBeanDefinition());
    }

    public void loadBean(Class<?> clazz, Map<String, Object> props) {
        BeanDefinitionBuilder userBeanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
        //  增加属性
        for (Entry<String, Object> entry : props.entrySet()) {
            userBeanDefinitionBuilder.addPropertyValue(entry.getKey(), entry.getValue());
        }
        // 增加对象
        //  userBeanDefinitionBuilder.addPropertyReference(name, beanName)
        DefaultListableBeanFactory acf = (DefaultListableBeanFactory) context.getAutowireCapableBeanFactory();
        acf.registerBeanDefinition(clazz.getName(), userBeanDefinitionBuilder.getRawBeanDefinition());

    }

    public void removeBean(String name) {
        DefaultListableBeanFactory acf = (DefaultListableBeanFactory) context.getAutowireCapableBeanFactory();
        if (acf.containsBean(name)) {
            acf.removeBeanDefinition(name);
        }
    }

    /**
     * 发送一个事件
     *  详解Spring事件驱动模型 http://jinnianshilongnian.iteye.com/blog/1902886
     * @param event
     */
    public void publishEvent(ApplicationEvent event) {
        context.publishEvent(event);
    }

    /**
     * 添加一个Listener
     * @param listener
     */
    public void addApplicationListener(ApplicationListener<?> listener) {
        context.addApplicationListener(listener);
    }

}
